6.3.3 创建帖子

在完成了数据库的初步设置之后,现在是时候创建我们的首条数据库记录了。本节还会用到之前几节展示过的 Post 结构,跟之前不一样的是,这次展示的程序将不会再把 Post 结构包含的信息存储到内存或者文件中,而是把这些信息存储到Postgres数据库中,并在需要的时候从数据库中获取这些信息。

前面的示例程序向我们展示了如何使用不同的函数执行数据的创建、获取、更新和删除操作,而在这一节,我们将会了解到使用 Create 函数创建新帖子的更多细节。在仔细研究 Create 函数的代码之前,让我们先来了解一下创建帖子的具体步骤。

创建帖子首先要做的是创建一个 Post 结构,并为该结构的 Content 字段和 Author 字段设置值。需要注意的是,因为结构的 Id 字段的值通常是由数据库的自增主键自动生成的,所以我们并不需要为这个字段设置值。

post := Post{Content: "Hello World!", Author: "Sau Sheong"}

如果我们现在使用一个 fmt.Println 语句打印这个结构,会看到 Id 字段的值被初始化成了 0

fmt.Println(post)

❶ {0 Hello World! Sau Sheong}

现在,我们可以通过执行 Post 结构的 Create 方法,把结构中包含的数据存储到数据库的记录(record)里面:

post.Create()

Create 方法在发生故障时会返回一个错误,但为了让代码保持简单,我们这里暂且先省略相应的错误处理代码。现在,再次打印这个 Post 结构:

fmt.Println(post)

❶ {1 Hello World! Sau Sheong}

从打印的结果可以看到, Id 字段的值现在被设置成了 1 。在了解了使用 Create 函数创建新帖子的具体步骤之后,现在是时候来看看代码清单6-8,了解一下它的具体实现代码了。

代码清单6-8 创建一篇帖子

func (post *Post) Create() (err error) {
  statement := "insert into posts (content, author) values ($1, $2)
  ➥returning id "
  stmt, err := db.Prepare(statement)
  if err != nil {
     return
  }
  defer stmt.Close()
  err = stmt.QueryRow(post.Content, post.Author).Scan(&post.Id)
  if err != nil {
     return
  }
  return
}

Create 函数是 Post 结构的一个方法,这一点可以通过 Create 函数的定义看出:在 func 关键字和函数名 Create 之间,有一个指向 Post 结构的引用,这个名为 post 的引用也被称为方法的接收者(receiver),接收者可以不使用 & 符号,直接在方法内部对结构进行引用。

Create 方法做的第一件事是定义一条SQL预处理语句,一条预处理语句(prepared statement)就是一个SQL语句模板,这种语句通常用于重复执行指定的SQL语句,用户在执行预处理语句时需要为语句中的参数提供实际值。

比如,在创建数据库记录的时候, Create 函数就会使用实际值去替换以下语句中的 $1$2

statement := "insert into posts (content, author) values ($1, $2) returning id"

除了在数据库里面创建记录之外,这个语句还会要求数据库返回 id 列的值,本文稍后就会说明这样做的具体原因。

为了创建预处理语句,程序使用了 sql.DB 结构的 Prepare 方法:

stmt, err := db.Prepare(statement)

这行代码会创建一个指向 sql.Stmt 接口的引用,这个引用就是上面提到的预处理语句。 sql.Stmt 接口的定义位于 sql.Driver 包当中,而具体的结构则由数据库驱动实现。

之后,程序会调用预处理语句的 QueryRow 方法,并把来自接收者的数据传递给该方法,以此来执行预处理语句:

err = stmt.QueryRow(post.Content, post.Author).Scan(&post.Id)

我们之所以在这里使用 QueryRow 方法,是因为我们只想要获取一个指向 sql.Row 结构的引用:如果 QueryRow 发现被执行的SQL语句返回了多于一个 sql.Row ,那么它只会返回结果中的第一个 sql.Row ,并丢弃剩余的所有 sql.Row 。因为 QueryRow 方法的返回值只有一个 sql.Row 结构,它不会返回任何错误,所以 QueryRow 方法通常会跟 Row 结构的 Scan 方法搭配使用,并由 Scan 方法把行中的值复制到程序为其提供的参数里面。正如上面的代码所示, Scan 方法会把SQL查询语句返回的 id 列的值设置为 post 接收者的 Id 字段的值,这也是我们前面在编写预处理语句时,要求SQL查询语句返回 id 列的值的原因。很明显,因为接收者的 Content 字段和 Author 字段都已经有值了,所以程序最后要做的就是将接收者的 Id 字段的值设置成数据库生成的自增整数。现在,正如你所料,因为 post 变量的 Id 字段也已经设置了值,所以程序得到的将是一个完整地进行了设置的 Post 结构,并且该结构包含的数据与数据库记录的数据完全一致。

results matching ""

    No results matching ""